/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010

    This file is part of Maxsi Distribution.

    Maxsi Distribution is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Distribution is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Distribution. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiDistributionContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiDistributionContributors.h. For more information please see
	MaxsiDistributionContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiDistribution.dll
	Distributing since 2008

	MaxsiTorrent.h
	Torrenting!

****************************************************************************/

#ifndef MaxsiTorrent_H
#define MaxsiTorrent_H
	
	// TODO CHANGE FILE SIZES FROM SIZE_T TO __INT64 SO WE SUPPORT REALLY LARGE STRUCTURES!

	// Protocol Versions.
	#define	MaxsiTorrent_1_0				(0x00000001)		//	MaxsiTorrent/1.0
	#define	MaxsiTorrent_MaxProtocol		MaxsiTorrent_1_0	//	Highest protocol supported (=default)
	#define	MaxsiTorrent_MinProtocol		MaxsiTorrent_1_0	//	Lowest protocol supported
	#define	MaxsiDistributionHTTP			(0x10000001)		//	MaxsiDistributionHTTP

	// Magic constants
	#define	MaxsiTorrent_BlockSize			(1048576)	//	1 MiB
	#define	MaxsiTorrent_SpeedRememberFor	(30)		//	30 seconds
	#define	MaxsiTorrent_SpeedDefaultSmooth	(4)			//	4 seconds

	// Modes
	#define Torrent_Handshake				(0x0000000)	//	The protocol has not been agreed on yet
	#define Torrent_CommandMode				(0x0000001)	//	Awaiting commands.
	#define Torrent_InCommand				(0x0000002)	//	Exectuing a command.
	
	#define TorrentHTTP_Ready				(0x0000000)	//	A HTTP query hasn't been sent yet.
	#define TorrentHTTP_Sent				(0x0000001)	//	A HTTP query has been sent. Do not send another.
	#define TorrentHTTP_Reconnect			(0x0000002)	//	The response is fully recieved, disconnect, reconnect, send another one


	// Commands
	#define Torrent_Disconnect				(0x00000000)
	#define Torrent_Error					(0x80000000)
	#define Torrent_QueryActiveTorrents		(0x00000001)
	#define Torrent_ReplyActiveTorrents		(0x80000001)
	#define Torrent_QueryTorrentStatus		(0x00000002)
	#define Torrent_ReplyTorrentStatus		(0x80000002)
	#define Torrent_srsly					(0xFFFFFFFF)	// Dude, srsly, what the hell is wrong with you? We better disconnect

	#define	Block_Locked					(0x00000001)	// This block is locked.
	#define	Block_Downloaded				(0x00000002)	// This block has been downloaded and is stored somewhere.
	#define	Block_Written					(0x00000004)	// This block has been written to its proper place.


	#define Torrent_MaxBlocksPerPeer		(16)


	// Windows Messages
	//#define	Torrent_MSG_LockBlock			(ME_Thread_Cmd_Custom+1) // Obsolete.
	#define	Torrent_MSG_SpeedUpdate			(ME_Thread_Cmd_Custom+2)

	// Classes
	class	ME_TorrentServer;
	class	ME_TorrentServerClient;
	class	ME_FileServer;
	class	MaxsiTorrentData;

	// Structs
	struct	MaxsiTorrentFile
	{
		char*				FileName;
		size_t				BlockBegin; // Num bytes into the block stream where this file begins
		size_t				BlockEnd; // Numbytes into the block stream where this file ends
		char				SHA1[40];
	};

	struct	MaxsiTorrentBlock
	{
		size_t				Id;
		int					Flags;		// Combination of Block_* flags!
		int					Available;	// num bytes available of the entire block.
		MaxsiTorrentData*	TorrentData;// Pointer to the TorrentData to which this block belongs.
		int					FileBegin;  // Id of the first file in the block.
		int					FileEnd;    // Id of the last file in the block. All files inbetween FileBegin and FileEnd is in this block.
		char				SHA1[40];

	};

	
	struct	MaxsiTorrentBlockLockRequest
	{
		MaxsiTorrentBlock*		Block;
		ME_TorrentServerClient*	Client;
		size_t					Slot;

	};

	#define ME_TorrentServerClientHTTP_ReadingStatusLine		(0x0000000)	//	Figuring out what's going on!
	#define ME_TorrentServerClientHTTP_ReadingHeaders			(0x0000001)	//	Processing HTTP Headers
	#define ME_TorrentServerClientHTTP_ReadingBody				(0x0000002)	//	Processing HTTP Body
	#define ME_TorrentServerClientHTTP_ShouldDisconnect			(0x0000003)	//	Processing HTTP Body


	struct	ME_TorrentServerClientHTTP
	{
		int		Step;
		int		Status;
		size_t	ChunkLen;
		size_t	DataRead;
		bool	Chunked;
		bool	ChunkWritten;
		bool	NeedsToReadCRLF;
		char	BlockTorrent[11 /* ceiling(32*ln(2)/ln(10))+1 */];
		char	BlockId[11 /* ceiling(32*ln(2)/ln(10))+1 */];
		char	BlockSize[11 /* ceiling(32*ln(2)/ln(10))+1 */];
		int		BlockStatus;
		int		BlockLeft;
		size_t	iBlockTorrent;
		size_t	iBlockId;
		size_t	iBlockSize;
	};

	class	MaxsiTorrentData
	{
	public:
		char*				TorrentName;
		char*				ContentArchive;
		char*				DirectoryName;
		size_t				nFiles;
		MaxsiTorrentFile**	pFiles;
		size_t				nBlocks;
		MaxsiTorrentBlock**	pBlocks;
		size_t				SpeedRecorded;
		__int64				SpeedCompressed[MaxsiTorrent_SpeedRememberFor];
		__int64				SpeedUncompressed[MaxsiTorrent_SpeedRememberFor];
		__int64				DataDownloaded;
		__int64				DataTotal;
		__int64				DataRequired; // DataTotal - all the data on the computer prior to the install
		__int64				ComThisSecond;
		__int64				UncomThisSecond;
		DWORD				SecondStart;
		DWORD				NextSpeedUpdate;
		CRITICAL_SECTION	MayWriteToBlocks;
		size_t				Id;
		ME_TorrentServer*	Server;

	public:
		size_t	GetBlockSize(size_t Id);

	public:
		MaxsiTorrentData();
		~MaxsiTorrentData();
		
		void Enter();
		void Leave();
	};


	class ME_TorrentServerClient : public ME_ServerClient
	{
		Declare_Class(ME_TorrentServerClient, ME_ServerClient);

	public:
		ME_TorrentServerClient();
		~ME_TorrentServerClient();

		virtual		size_t	GetBlockSize();
		virtual		bool	Init();				// Attach to a new client
		virtual		bool	Close();			// Detach from a client
		virtual		bool	InitializeBuffers();
		//virtual		bool	OnBlockLocked(size_t Slot); // When a block is locked. Run from the main thread!

		//virtual		bool	OnRecieved(size_t Length); // When Length bytes are recieved
		virtual		bool	OnBlockData(char* p, size_t Len);
		virtual		bool	OnFullBlock(size_t LocalId);

		virtual		bool	Connect(char* Host);
		virtual		bool	Connected();
		virtual		bool	Disconnect();
		virtual		bool	Disconnected();

		// TODO: Perhaps move to MaxsiEngine::ME_ServerClient?
		virtual		bool	Think();

		virtual		bool	MaxsiTorrentThink();
		virtual		bool	MaxsiDistributionHTTPThink();
		virtual		bool	MaxsiDistributionHTTPReadHTTPResponse();
		virtual		bool	MaxsiDistributionHTTPReadHTTPChunkedBody();
		virtual		bool	MaxsiDistributionHTTPReadHTTPBody();

		virtual		bool	IsAnyBlocksFree();
		virtual		bool	HasLockedBlocks();
		virtual		size_t	NumLockedBlocks();
		virtual		size_t	NumUnlockedBlocks();
		virtual		size_t	GetFreeBlock();
		virtual		bool	RequestBlocks();
		virtual		bool	RequestBlocks(size_t Num);
		virtual		size_t	HowManyBocksShouldBeRequested();
		virtual		bool	RemoteHasBlock(size_t Num);

		virtual		bool	SendBlockRequests();
		virtual		bool	SendBlockRequestsMaxsiTorrent();
		virtual		bool	SendBlockRequestsMaxsiDistributionHTTP();

		virtual		bool	RecievedNBytes(size_t Compressed, size_t Uncompressed, size_t TorrentId);

	public:

		virtual		bool	SetProtocol(int Protocol);
		virtual		int		GetProtocol();

	private:
		int			m_Protocol;
		int			m_Mode;
		int			m_Command;

	public:
		char*		Remote;
		//bool		IsConnecting;
		//bool		IsConnected;
		//bool		ShouldBeConnected;
		
		__int64		ComThisSecond;
		__int64		UncomThisSecond;
		__int64		ComDownSpeed;
		__int64		UncomDownSpeed;
		DWORD		SecondStart;

		// Todo: Obsolete. Delete:
		//size_t		TorrentId;	// The which torrent on the torrent server do we use!

		// TODO: TimeStamp of last recieved Torrent_ReplyTorrentStatus command

		// The blocks that this peer is handling right now,
		// May only be written to by the main thread
		// May only be read if IsBlockSecure[i] == true.
		MaxsiTorrentBlock*		LockedBlocks[Torrent_MaxBlocksPerPeer];
		// May be set to true by the main thread
		// May be set to false by the worker thread
		// Todo: the above permissions are outdated!
		bool					IsBlockSecure[Torrent_MaxBlocksPerPeer];
		// May only be acccessed by the worker thread
		char*					PeerData;

		ME_TorrentServerClientHTTP	HTTP;

	};

	// Overlapped I/O: Completion Ports
	class ME_TorrentServer : public ME_IOCPServer
	{
		Declare_Class(ME_TorrentServer, ME_IOCPServer);
	public:
		ME_TorrentServer();
		~ME_TorrentServer();

	public:
		virtual	bool	HandleRequest		(ME_ServerClient* Client, char* Parameter = 0);
		//virtual	bool	LockBlock			(MaxsiTorrentBlock* Block, ME_TorrentServerClient*	Requester, size_t Slot);
		virtual	bool	OnBlockFailed		(MaxsiTorrentBlock* Block);
		virtual	bool	OnBlockSuccess		(MaxsiTorrentBlock* Block);

		virtual	bool	EvaluateServer		();	// See if all torrents has been downloaded and then shut down.

		//virtual	bool	CallbackLockBlock	(MaxsiTorrentBlock* Block, ME_TorrentServerClient*	Requester, size_t Slot);

		virtual	bool	RunServer(short Port);
		virtual	bool	RunServerEx(short Port, size_t NumConnections, size_t CheckTime);

		virtual			ME_ServerClient*	CreateIOCPClient		(void);
		virtual			bool				OnClientCreated			(ME_ServerClient* Client);
		virtual			bool				OnClientConnected		(SOCKET s, int Errror);
		virtual			bool				OnClientDisconnected	(SOCKET s, int Errror);

	public:
		MaxsiTorrentData**	TorrentData;
		size_t				nTorrents;
		ME_FileServer*		FileSystem;
		List				Peers;
		size_t				PeersCreated;

		// Speed data
		virtual	bool		AddSpeed				(size_t Compressed, size_t Uncompressed, size_t TorrentId);
		virtual	__int64		GetCompressedSpeed		(size_t TorrentId, size_t	TimeAgo, size_t Smooth);
		virtual	__int64		GetUncompressedSpeed	(size_t TorrentId, size_t	TimeAgo, size_t Smooth);
		virtual	__int64		DataDownloaded			(size_t TorrentId);
		virtual	__int64		DataTotal				(size_t TorrentId);
		virtual	__int64		DataRequired			(size_t TorrentId);

	public:
		// Callback functions!
		DECLARE_FUNCTION(bool,	OnSpeedUpdate,			(ME_TorrentServer* Updated, size_t	TorrentId));
		DECLARE_FUNCTION(bool,	OnBufferExpanded,		(ME_FileServer* Updated));
		virtual	bool			OnBufferExpandedFunc	(ME_FileServer* Updated);

	};

	const	UINT						ME_TorrentMessageIDFunc	(void);
	#define	ME_TorrentMessageID			ME_TorrentMessageIDFunc	()
	
#endif